﻿using System.Collections.Generic;
using System.Data;
using System.Text;

namespace smp.QueryDb.Structure
{
    public class Table
    {
        private readonly List<Join> _joins = new List<Join>();
        private string _fromStatement;
        
        private readonly List<Column> _columns = new List<Column>();

        public Table(string name)
        {
            Name = name;
            Declaration = string.Format("[{0}]", name);
            _fromStatement = Declaration;
        }

        public Table(string schemaName, string name)
        {
            Name = name;
            Schema = schemaName;
            Declaration = string.Format("[{0}].[{1}]", schemaName, name);
            _fromStatement = Declaration;
        }

        public Table(string dataBaseName, string schemaName, string name)
        {
            Name = name;
            Database = dataBaseName;
            Schema = schemaName;
            Declaration = string.Format("[{0}].[{1}].[{2}]", dataBaseName, schemaName, name);
            _fromStatement = Declaration;
        }

        internal string Name { get; private set; }
        internal string Declaration { get; private set; }
        internal bool IsReadOnly { get; set; }
        internal Id Id { get; set; }
        internal List<Column> Columns { get { return _columns; } }
        internal string Database { get; private set; }
        internal string Schema { get; private set; }
        internal string FromStatement
        {
            get
            {
                var statement = new StringBuilder();
                statement.Append(_fromStatement);
                foreach(var join in _joins)
                {
                    statement.Append(join.JoinDeclaration);
                }
                _joins.Clear();
                return statement.ToString();
            }
        }

        internal bool IsTableDeclared(string tableDeclaration)
        {
            if (Declaration == tableDeclaration)
                return true;
            if (_joins.Count == 0)
                return false;
            foreach (var join in _joins)
            {
                if (join.IsTableDeclared(tableDeclaration))
                    return true;
            }
            return false;
        }

        public Table Join(Join join)
        {
            join.FiniliseJoinWithBaseDeclaration(Declaration);
            _joins.Add(join);
            return this;
        }

        public Table OuterJoin(Join join)
        {
            join.SetIsOuter();
            join.FiniliseJoinWithBaseDeclaration(Declaration);
            _joins.Add(join);
            return this;
        }

        public Table As(string name)
        {
            _fromStatement = _fromStatement + " AS [" + name + "]";
            return this;
        }

        protected Column AddColumn(string columnName, DbType dataType)
        {
            if (_columns.Find(x => x.Name == columnName) != null)
            {
                throw Helper.Error("Table {0}, cannot have two columns of the same name {1}",
                             Declaration,
                             columnName);
            }
            var column = new Column(this, columnName, dataType);
            _columns.Add(column);
            return column;
        }

        protected Column AddColumn(string columnName, DbType dataType, int fieldLength)
        {
            if (_columns.Find(x => x.Name == columnName) != null)
            {
                throw Helper.Error("Table {0}, cannot have two columns of the same name {1}",
                             Declaration,
                             columnName);
            }
            var column = new Column(this, columnName, dataType, fieldLength);
            _columns.Add(column);
            return column;
        }

        protected Column AddColumn(string columnName, DbType dataType, bool isNullable)
        {
            if (_columns.Find(x => x.Name == columnName) != null)
            {
                throw Helper.Error("Table {0}, cannot have two columns of the same name {1}",
                             Declaration,
                             columnName);
            }
            var column = new Column(this, columnName, dataType, isNullable);
            _columns.Add(column);
            return column;
        }

        protected Column AddColumn(string columnName, DbType dataType, bool isNullable, int fieldLength)
        {
            if (_columns.Find(x => x.Name == columnName) != null)
            {
                throw Helper.Error("Table {0}, cannot have two columns of the same name {1}",
                             Declaration,
                             columnName);
            }
            var column = new Column(this, columnName, dataType, isNullable, fieldLength);
            _columns.Add(column);
            return column;
        }

        protected Id SetId(string columnName, DbType dataType)
        {
            if (Id != null)
            {
                throw Helper.Error("Table {0}, cannot contain more than one Id property, First : {1}, second {2}",
                    Declaration,
                    Id.Name,
                    columnName);
            }
            Id = new Id(this, columnName, dataType);
            return Id;
        }

        protected Id SetId(string columnName, DbType dataType, bool isIdDataBaseGenerated)
        {
            if (Id != null)
            {
                throw Helper.Error("Table {0}, cannot contain more than one Id property, First : {1}, second {2}",
                    Declaration,
                    Id.Name,
                    columnName);
            }
            Id = new Id(this, columnName, dataType, isIdDataBaseGenerated);
            return Id;
        }

        protected Id SetId(string columnName, DbType dataType, IdGenerationType idGenerationType)
        {
            if (Id != null)
            {
                throw Helper.Error("Table {0}, cannot contain more than one Id property, First : {1}, second {2}",
                    Declaration,
                    Id.Name,
                    columnName);
            }
            Id = new Id(this, columnName, dataType, idGenerationType);
            return Id;
        }
    }
}
